
import { getRoomDomain, getSources } from "../../../global/map";
import { jobGoto } from "../../../job/goto";
import { jobSuicide } from "../../../job/suicide";
import { getApproprateEnergy, getMaxEnergy, spawnCreep } from "../../../role/spawn";
import { errLog, stdlog } from "../../../util/log";
import { GroupHarvesters } from "../terminal/harvesters";
import { GroupCluster } from "./cluster";
import { ClusterCore } from "./core";


export class ClusterHarvesters extends GroupCluster{
    /**
     * 
     * @param {Room} spawnRoom 
     */
    constructor(spawnRoom=undefined){
        super(spawnRoom);
        this.harvesters=[];
        this.reserverName = undefined;
    }
    get maxCount(){
        return 4;
    }
    get count(){
        let ans = 0;
        for(let harvesterGroup of this.harvesters){
            if(harvesterGroup.size===0){
                continue;
            }
            ans++;
        }
        return ans;
    }
    get reserver(){
        if(this._reserver === null){
            return undefined;
        }
        if(this._reserver !== undefined){
            return this._reserver;
        }

        let creep = Game.creeps[this.reserverName];
        if(!creep){
            this._reserver = creep = null;
            this.reserverName = undefined;
            return undefined;
        }
        return this._reserver = creep;
    }
    serialize(){
        let harvesters=[];
        for(let harvesterGroup of this.harvesters){
            harvesters.push(harvesterGroup.serialize());
        }
        return {
            super:super.serialize(),
            harvesters: harvesters,
            reserverName: this.reserverName,
        };
    }
    deserialize(obj){
        this.harvesters=[]
        super.deserialize(obj.super);
        for(let harvesterGroup of obj.harvesters){
            this.harvesters.push(new GroupHarvesters().deserialize(harvesterGroup));
        }
        this.reserverName = obj.reserverName;
        return this;
    }
    generateGroup(){
        if(this.count<this.maxCount){
            let mappedSources = [];
            for(let group of this.harvesters){
                mappedSources.push(group.sourceId);
            }
            let sourceIds=[];
            for(let name of getRoomDomain(this.spawnRoom)){
                let room=Game.rooms[name];
                if(!room){
                    continue;
                }
                if(room.find(FIND_HOSTILE_STRUCTURES).some((structure)=>structure.owner.username === "Source Keeper")){
                    continue;
                }
                sourceIds.push(...getSources(room).filter((sourceId)=>!mappedSources.some((id)=>id == sourceId)));
            }
            if(sourceIds.length === 0){
                return;
            }
            let target;
            let targetCost;

            for(let id of sourceIds){
                let source = Game.getObjectById(id);
                if(!(source instanceof Source)){
                    continue;
                }

                let cost;
                const getCost = (targetPos)=>{
                    return PathFinder.search(this.spawnRoom.controller.pos, targetPos, {maxCost:100}).cost;
                };
                if(!target){
                    target = source;
                }else if(target.room.name !== this.spawnRoomName && source.room.name === this.spawnRoomName){
                    target = source;
                }else if((cost = getCost(source))<targetCost){
                    target = source;
                }

                if(target === source && !cost){
                    targetCost = getCost(source);
                }
            }
            if(!target){
                return;
            }
            let targetId=target.id;
            this.harvesters.push(new GroupHarvesters(this.spawnRoom,targetId));
        }
    }
    groupSpawn(){
        let harvesterGroup = this.harvesters[0];
        for(let i=1; i<this.harvesters.length; i++){
            let group = this.harvesters[i];
            if(!(group instanceof GroupHarvesters)){
                continue;
            }
            if(group.size >= group.maxSize){
                continue;
            }
            if((this.harvesters[i].size/this.harvesters[i].maxSize<harvesterGroup.size/harvesterGroup.maxSize) || (harvesterGroup.targetRoomName !== this.spawnRoomName && this.harvesters[i].targetRoomName === this.spawnRoomName)){
                harvesterGroup = this.harvesters[i];
            }
        }
        if(harvesterGroup.size >= harvesterGroup.maxSize){
            stdlog("harvester full");
            return ERR_FULL;
        }
        // console.log('fdsa');
        return harvesterGroup.spawn(this.size);
    }
    getReserverTargetRoom(){
        if(this._reserverTargetRoom === null){
            return undefined;
        }
        if(this._reserverTargetRoom !== undefined){
            return this._reserverTargetRoom;
        }

        let targetRoom = undefined;
        if(targetRoom === undefined){
            for(let group of this.harvesters){
                if(!(group instanceof GroupHarvesters)){
                    continue;
                }
                if(!group.targetRoomName || !group.targetRoom){
                    continue;
                }
                if(group.size == 0){
                    continue;
                }
                if(!group.isSourceReserved()){
                    targetRoom = group.targetRoom;
                    break;
                }
            }
        }

        if(targetRoom === undefined){
            this._reserverTargetRoom = null;
            return undefined;
        }
        return this._reserverTargetRoom = targetRoom;
    }
    canSpawnReserver(){
        if(this.getReserverTargetRoom() === undefined){
            return false;
        }
        if(this.maxCount>this.count+1){
            return false;
        }
        return (this.size>1 || (this.size>0 && this.maxSize<2)) && this.count>0 && 
            this.reserver === undefined && 
            getMaxEnergy(this.spawnRoom)>=1300;
    }
    spawnReserver(){
        let newCreep = spawnCreep(this.spawnRoom, {
            require: [CLAIM,MOVE],
            claim: .50,
            move: .50,
        },{
            memory: {
                role: 'reserver',
            }
        }, Math.min(getApproprateEnergy(this.spawnRoom), 7800));
        if(!(newCreep instanceof Creep)){
            return newCreep;
        }
        this.reserverName = newCreep.name;
        return OK;
    }
    /**
     * 
     * @returns {CreepActionReturnCode}
     */
    spawn(){
        if(this.harvesters.length === 0){
            this.generateGroup();
        }

        if(this.harvesters.length===0){
            errLog(new Error("no group"));
            return ERR_FULL;
        }
        if(this.canSpawnReserver()){
            return this.spawnReserver();
        }else{
            return this.groupSpawn();
        }
    }
    get size(){
        let ans=0;
        for(let group of this.harvesters){
            ans+=group.size;
        }
        if(this.reserver){
            ans++;
        }
        return ans;
    }
    get maxSize(){
        if(this._maxSize !== undefined){
            return this._maxSize;
        }
        let ans = this.maxCount*GroupHarvesters.getGroupMaxSize(this.spawnRoom);
        if(this.canSpawnReserver()){
            ans++;
        }
        return this._maxSize = ans;
    }
    /**
     * 
     * @param {boolean} forceStay 
     */
    run(forceStay=false){
        if(this.harvesters.length<this.maxCount){
            this.generateGroup();
        }
        let colony = Game.groups[this.spawnRoomName];
        let coreLink = undefined;
        if(colony && (colony.core instanceof ClusterCore) && colony.core.isActive()){
            coreLink = colony.core.links[0];
        }
        for(let i=0; i<this.harvesters.length; i++){
            let group = this.harvesters[i];
            if(!(group instanceof GroupHarvesters)){
                continue;
            }
            if(!group.sourceId){
                this.harvesters.splice(i,1);
                i--;
                throw new Error('sourceId not defined');
            }
            if(group.size===0 && group.source && Memory.rooms[group.source.room.name].enemy){
                this.harvesters.splice(i,1);
                i--;
                errLog(new Error("enemy room"));
                continue;
            }
            let targetLink = undefined;
            if(Boolean(group.source) && group.source.room.name === this.spawnRoomName){
                targetLink = coreLink;
            }
            group.run(forceStay, targetLink);
        }
        this.reserverRun();
    }
    reserverRun(){
        let creep = this.reserver;
        if(!creep){
            return ERR_NOT_FOUND;
        }
        let targetRoom = undefined
        if(creep.memory.targetRoomName !== undefined){
            targetRoom = Game.rooms[creep.memory.targetRoomName];
        }
        if(targetRoom === undefined){
            targetRoom = this.getReserverTargetRoom();
        }
        if(targetRoom === undefined){
            return jobSuicide.run(creep);
        }else{
            creep.memory.targetRoomName = targetRoom.name;
        }
        let ret = creep.reserveController(targetRoom.controller);
        if(ret === ERR_INVALID_TARGET){
            ret = creep.attackController(targetRoom.controller);
        }
        if(ret === ERR_NOT_IN_RANGE){
            ret = jobGoto.run(creep, [targetRoom.controller], {visualizePathStyle:{stroke:"#ffaa00"}});
        }
        if(targetRoom.controller.reservation && targetRoom.controller.reservation.ticksToEnd===5000){
            creep.memory.targetRoomName = undefined;
        }
        return ret;
    }
}